// Includes
#include "CStaticMesh.h"
#include "TextureLoaders.h"			// Texture loader functions

#include <cctype>		// C library type header
#include <iostream>		// C++ I/O stream library

using namespace std;

namespace EraAstral
{
	namespace graphics
	{
		// Static mesh constructor
		StaticMesh::StaticMesh()
			: IMesh()
		{
			// Generate display list index
			dispListIndex = glGenLists(1);
		}
		
		// Constructor creating mesh from vertex, normal, tex coord and index data
		StaticMesh::StaticMesh(Vector3 **vertices, Vector3 **normals, Vector2 **texCoords, IndexStruct **indices,
							unsigned int vertexCount, unsigned int normalCount, unsigned int texCoordCount, 
							unsigned int indexCount)
			: IMesh(vertexCount, normalCount, texCoordCount, indexCount)
		{
			// Generate display list index
			dispListIndex = glGenLists(1);
			
			// Set data
			m_vertices = *vertices;
			m_normals = *normals;
			if (*texCoords == NULL)
				m_hasTexture = false;
			else
			{
				m_hasTexture = true;
				m_texCoords = *texCoords;
			}
			m_indices = *indices;
			
			// Commit mesh data to OpenGL
			Commit();	
		}
		
		// Static mesh destructor
		StaticMesh::~StaticMesh()
		{
			// Delete OpenGL display list
			glDeleteLists(dispListIndex, 1);
		}
		
		// Loads a texture for the mesh from a file
		int StaticMesh::LoadTexture(const char *fileName)
		{		
			// Determine extension of the file
			int dotPosition = -1;
			char *lowerCaseFileName = new char[strlen(fileName) + 1];
			// Add string end symbol
			lowerCaseFileName[strlen(fileName)] = '\0';
			// Loop through to find dot
			for (unsigned int i = 0; i < strlen(fileName); i++)
			{
				// Convert to lower case letters
				lowerCaseFileName[i] = tolower(fileName[i]);	
			
				if (fileName[i] == '.')
					dotPosition = i;
			}
			
			// Check if no dot was found
			if (dotPosition == -1)
			{
				cout << "Error loading texture file. No file extension found." << endl;
				// Free memory used for lower case file name
				delete[] lowerCaseFileName;
				return 1;
			}
			
			// Check extension
			if (strncmp(lowerCaseFileName + dotPosition, ".jpg", 4) == 0 ||
						strncmp(lowerCaseFileName + dotPosition, ".jpeg", 5) == 0)
			{
				cout << "Texture file identified as JPEG." << endl;
				// Load JPEG texture file
				m_texture = LoadJPG(fileName);
			}
			else if (strncmp(lowerCaseFileName + dotPosition, ".bmp", 4) == 0)
			{
				cout << "Texture file identified as BMP." << endl;
				// Load BMP texture file
				m_texture = LoadBMP(fileName);
			}
			else if (strncmp(lowerCaseFileName + dotPosition, ".png", 4) == 0)
			{
				cout << "Texture file identified as PNG." << endl;
				// Load PNG texture file
				m_texture = LoadPNG(fileName);
			}
			else
			{
				// Unknown texture file format
				cout << "Error loading texture: File format not recognized." << endl;
				return 2;
			}
			
			// Check if texture was loaded successfully
			if (m_texture == NULL)
			{
				// Error while loading
				cout << "Could not load texture from file " << fileName << endl;
				return 3;
			}
			else
			{
				// Commit changes and generate OpenGL texture
				m_texture->Commit();
				m_hasTexture = true;
			}
				
			// Free memory used for lower case file name
			delete[] lowerCaseFileName;
			
			// Return successfully
			return 0;
		}
		
		// Commits mesh changes to OpenGL
		int StaticMesh::Commit()
		{		
			// Start display list
			glNewList(dispListIndex, GL_COMPILE);
				
				// Begin drawing sequence
				glBegin(GL_TRIANGLES);
				
					// Set white color
					glColor3f(1.0f, 1.0f, 1.0f);				
				
					// Loop through indices
					for (unsigned int i = 0; i < m_indexCount; i++)
					{
						// Check if the mesh has normals
						if (m_hasNormals)
						{
							// Set normal
							glNormal3fv(m_normals[m_indices[i].normal].GetData());
						}
							
						// Check if the mesh has a texture
						if (m_hasTexture)
							// Set texture coordinate
							glTexCoord2fv(m_texCoords[m_indices[i].texCoord].GetData());
						
						// Draw vertex
						glVertex3fv(m_vertices[m_indices[i].vertex].GetData());
					}
				
				// End drawing sequence
				glEnd();
			
			// End display list
			glEndList();
		
			// Return successfully
			return 0;
		}
		
		// Draw the mesh
		void StaticMesh::Draw()
		{
			// Clear bound texture
			glBindTexture(GL_TEXTURE_2D, 0);		
		
			// Enable texturing if the mesh has a texture
			if (m_hasTexture && m_texture != NULL)
			{
				glEnable(GL_TEXTURE_2D);
				m_texture->Bind();
			}
				
			// Call OpenGL display list
			glCallList(dispListIndex);
				
			// Disable texturing
			if (m_hasTexture)
				glDisable(GL_TEXTURE_2D);
		}
	}
}
